﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Linq;
using System.Windows.Media;

namespace GameAnnotation
{
  public class Move
  {
    public int Number { get; set; }
    public string Notation { get; set; }
    public bool IsBlackMove { get; set; }
    public bool IsSequential { get; set; }

    public Move(int number, string notation)
    {
      Number = number;
      Notation = notation;
    }
  }
  public class Node
  {
    public List<object> Values { get; set; }

    public Node(params object[] values)
    {
      Values = GetValue(values).ToList();
      SetIsSequential();
    }
    private void SetIsSequential()
    {
      for (var i = 1; i < Values.Count; i++)
      {
        var move = Values[i] as Move;
        if (move != null)
          move.IsSequential = Values[i - 1] is Move;
      }
    }

    private static IEnumerable<object> GetValue(IEnumerable<object> values)
    {
      foreach (var value in values)
      {
        var str = value as string;
        if (str != null)
        {
          yield return new Text(str);
          continue;
        }
        var node = value as Node;
        if (node != null)
        {
          yield return node;
          continue;
        }
        var move = value as Move;
        if (move != null)
        {
          if (move.Notation.StartsWith(".."))
          {
            move.Notation = move.Notation.Substring(2);
            move.IsBlackMove = true;
          }
          yield return move;
          continue;
        }
        throw new Exception();
      }
    }
  }
  public class Text
  {
    public string Value { get; set; }

    public Text(string value)
    {
      Value = value;
    }

  }
  public partial class MainWindow
  {
    public Node First = new Node(new Move(18, "..f4"),
      "Нет. Не решился. Видимо, причина в том, что Магнус - " +
      "тонкий позиционный шахматист, который очень остро переживает " +
      "наличие хороших опорных пунктов для фигур соперника.",
      new Node("Речь о варианте", new Move(18, "..b5!"), new Move(19, "exf5"),
        new Move(19, "..Bxf5+"), new Node("не столь сильно", new Move(19, "..gxf5")),
        new Move(20, "Bd3"), new Node("или", new Move(20, "Ne4"), new Move(20, "..Qb6"),
          "и", new Move(21, "..Nd6")), new Move(20, "..Qd7"),
          "- далее черные ставят коня на d6 и создают немилосердное давление " +
          "на ферзевом фланге. Полагаю, обладание пунктом e4 не гарантировало " +
          "здесь белым комфорта."),
          new Move(19, "Bf2"), new Move(19, "..Rab8"), new Move(20, "h4"),
          new Move(20, "..b5"), new Move(21, "Rc1"), "Резонный вопрос, уже озвученный мною: " +
          "\"А что дальше?\". Что дальше черным делать с пешкой b5? В случае размена на с4 " +
          "слабости на a6 и с5 станут губительными. " +
          "Если сыграть b5-b4, то о вскрытии ферзевого фланга придется забыть, а белый конь " +
          "отправится на d3 - с давлением на с5.",
          new Move(21, "..Rfc8"));


    public MainWindow()
    {
      InitializeComponent();

      new Writer(_doc).Write(First);

    }
  }

  public class Writer
  {
    private static readonly Style MoveStyle =
      new Style(typeof(TextElement));

    private static readonly Style[] MoveStyles =
      new[]
        {
          new Style(typeof (TextElement), MoveStyle)
            {
              Setters = { new Setter(TextElement.FontWeightProperty, FontWeights.Bold) }
            },
          new Style(typeof (TextElement), MoveStyle),
          new Style(typeof (TextElement), MoveStyle)
            {
              Setters = { new Setter(TextElement.FontStyleProperty, FontStyles.Italic) }
            }
        };

    private static readonly Style[] ParagraphStyles =
      new[]
        {
          new Style(typeof (Block))
            {
              Setters = { new Setter(Block.MarginProperty, new Thickness(0, 0, 0, 0)) }
            },
          new Style(typeof (Block))
            {
              Setters = { new Setter(Block.MarginProperty, new Thickness(20, 0, 0, 0)) }
            },
          new Style(typeof (Block))
            {
              Setters = { new Setter(Block.MarginProperty, new Thickness(40, 0, 0, 0)) }
            }
        };

    private static readonly Style TextStyle =
      new Style(typeof (TextElement))
        {
          Setters = {new Setter(TextElement.ForegroundProperty, Brushes.Brown)}
        };

    private readonly FlowDocument _doc;
    private int _currentLevel;
    private Paragraph _paragraph;

    public Writer(FlowDocument doc)
    {
      _doc = doc;
    }
    public void Write(Node node)
    {
      NextParagraph();
      WriteImpl(node);
      _doc.Blocks.Add(_paragraph);
    }

    private void WriteImpl(Node node)
    {
      foreach (var v in node.Values)
      {
        var move = v as Move;
        if (move != null)
        {
          WriteMove(move);
          continue;
        }
        var text = v as Text;
        if (text != null)
        {
          Write(false, true, false, new Run(text.Value));
          continue;
        }
        var child = v as Node;
        if (child != null)
        {
          _currentLevel++;
          NextParagraph();
          Write(true, false, false, new Run(Braces[_currentLevel][0]));
          WriteImpl(child);
          Write(true, false, false, new Run(Braces[_currentLevel][1]));
          _currentLevel--;
          continue;
        }
      }
    }

    private static readonly string[][] Braces = new[]
                                  {
                                    new[] {"", ""},
                                    new[] {"[", "]"},
                                    new[] {"(", ")"},
                                    new[] {"(", ")"}
                                  };

    private void WriteMove(Move move)
    {
      if (move.IsSequential)
      {
        Write(true, true, true, move.IsBlackMove
          ? new Run(move.Notation)
          : new Run(move.Number + "." + move.Notation));
      }
      else
      {
        NextParagraph();
        Write(true, true, true, move.IsBlackMove
          ? new Run(move.Number + "..." + move.Notation)
          : new Run(move.Number + "." + move.Notation));
      }
    }

    private void Write(bool useMoveStyle, bool addSpaces, bool isFocusable, Inline inline)
    {
      if (addSpaces)
        if (_paragraph.Inlines.Count != 0)
          _paragraph.Inlines.Add(" ");
      inline.Style = useMoveStyle ? MoveStyles[_currentLevel] : TextStyle;
      inline.Focusable = isFocusable;
      _paragraph.Inlines.Add(inline);
    }
  
    private void NextParagraph()
    {
      if (_paragraph != null) _doc.Blocks.Add(_paragraph);
      _paragraph = new Paragraph
                     {
                       Style = ParagraphStyles[_currentLevel]
                     };
    }
  }
}
